﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Admin.NET.Core.Service;
using Microsoft.AspNetCore.Http;
using Furion.DatabaseAccessor;
using Furion.FriendlyException;
using Mapster;
using SqlSugar;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Admin.NET.Application.Entity;
namespace Admin.NET.Application;

/// <summary>
/// 物资信息表服务 🧩
/// </summary>
[ApiDescriptionSettings(ApplicationConst.GroupNameStore, Order = 100)]
public partial class StoreMaterialsService : IDynamicApiController, ITransient
{
    private readonly SqlSugarRepository<StoreMaterials> _storeMaterialsRep;
    private readonly ISqlSugarClient _sqlSugarClient;
    // 自己添加的分类
    private readonly StoreTypeService _storeTypeService;

    public StoreMaterialsService(SqlSugarRepository<StoreMaterials> storeMaterialsRep, ISqlSugarClient sqlSugarClient, StoreTypeService storeTypeService)
    {
        _storeMaterialsRep = storeMaterialsRep;
        _sqlSugarClient = sqlSugarClient;
        _storeTypeService = storeTypeService;
    }

    #region 后建

    /// <summary>
    /// 构建分类完整路径
    /// </summary>
    /// <param name="storeTypeId">分类ID</param>
    /// <returns>完整路径，如：易耗品/薄板车间专用</returns>
    [ApiDescriptionSettings(Name = "BuildCategoryPathAsync"), HttpPost]
    public async Task<string> BuildCategoryPathAsync(long? storeTypeId)
    {
        if (storeTypeId == null) return string.Empty;

        var path = new List<string>();
        var currentId = storeTypeId;

        // 递归查找所有父级分类
        while (currentId.HasValue)
        {
            var storeType = await _sqlSugarClient.Queryable<StoreType>()
                .FirstAsync(x => x.Id == currentId.Value);
            if (storeType == null) break;

            path.Insert(0, storeType.Name); // 插入到最前面，确保正确的层级顺序
            currentId = storeType.Pid;
        }

        return string.Join("/", path);
    }

    /// <summary>
    /// 批量构建分类完整路径 - 性能优化版本 (防止N+1查询)
    /// </summary>
    /// <param name="storeTypeIds">分类ID集合</param>
    /// <returns>分类ID到完整路径的映射</returns>
    public async Task<Dictionary<long, string>> BatchBuildCategoryPathsAsync(List<long> storeTypeIds)
    {
        if (storeTypeIds == null || storeTypeIds.Count == 0)
            return new Dictionary<long, string>();

        var result = new Dictionary<long, string>();
        var distinctIds = storeTypeIds.Distinct().ToList();

        // 一次性查询所有需要的分类数据
        var allStoreTypes = await _sqlSugarClient.Queryable<StoreType>()
            .ToListAsync();

        if (allStoreTypes == null || allStoreTypes.Count == 0)
            return distinctIds.ToDictionary(id => id, id => string.Empty);

        // 在内存中构建路径 (没有数据库查询)
        foreach (var id in distinctIds)
        {
            var path = new List<string>();
            var currentId = (long?)id;

            // 递归查找所有父级分类 (从内存中查找，不查询数据库)
            while (currentId.HasValue)
            {
                var storeType = allStoreTypes.FirstOrDefault(x => x.Id == currentId.Value);
                if (storeType == null) break;

                path.Insert(0, storeType.Name);
                currentId = storeType.Pid;
            }

            result[id] = string.Join("/", path);
        }

        return result;
    }

    /// <summary>
    /// 构建分类完整路径（同步版本，用于导出模板）
    /// </summary>
    /// <param name="storeTypeId">分类ID</param>
    /// <param name="allStoreTypes">所有分类数据</param>
    /// <returns>完整路径，如：易耗品/薄板车间专用</returns>
    [ApiDescriptionSettings(Name = "BuildCategoryPath"), HttpPost]
    public string BuildCategoryPath(long storeTypeId, List<StoreType> allStoreTypes)
    {
        var path = new List<string>();
        var currentId = (long?)storeTypeId;

        // 递归查找所有父级分类
        while (currentId.HasValue)
        {
            var storeType = allStoreTypes.FirstOrDefault(x => x.Id == currentId.Value);
            if (storeType == null) break;

            path.Insert(0, storeType.Name); // 插入到最前面，确保正确的层级顺序
            currentId = storeType.Pid;
        }

        return string.Join("/", path);
    }

    #endregion

    #region 分类层级处理

    /// <summary>
    /// 获取指定分类及其所有下级分类的ID列表
    /// </summary>
    /// <param name="categoryId">分类ID</param>
    /// <returns>包含指定分类及其所有下级分类的ID列表</returns>
    private async Task<List<long>> GetCategoryAndChildrenIds(long categoryId)
    {
        var categoryIds = new List<long> { categoryId };
        
        // 递归获取所有下级分类
        await GetChildrenCategoryIds(categoryId, categoryIds);
        
        return categoryIds;
    }

    /// <summary>
    /// 递归获取下级分类ID
    /// </summary>
    /// <param name="parentId">父分类ID</param>
    /// <param name="categoryIds">分类ID列表（引用传递）</param>
    private async Task GetChildrenCategoryIds(long parentId, List<long> categoryIds)
    {
        // 查询所有下级分类
        var children = await _sqlSugarClient.Queryable<StoreType>()
            .Where(x => x.Pid == parentId && x.Status == StatusEnum.Enable)
            .Select(x => x.Id)
            .ToListAsync();

        if (children != null && children.Any())
        {
            foreach (var childId in children)
            {
                categoryIds.Add(childId);
                // 递归获取下级的下级分类
                await GetChildrenCategoryIds(childId, categoryIds);
            }
        }
    }

    #endregion

    #region 库存状态计算

    /// <summary>
    /// 计算库存状态
    /// </summary>
    /// <param name="currentStock">当前库存</param>
    /// <param name="minStock">最小库存阈值</param>
    /// <param name="maxStock">最大库存阈值</param>
    /// <returns>库存状态</returns>
    private StockStatusEnum CalculateStockStatus(decimal currentStock, int? minStock, int? maxStock)
    {
        // 如果未设置最小库存阈值，默认返回正常状态
        if (!minStock.HasValue)
        {
            return StockStatusEnum.Normal;
        }

        // 缺货：当前库存为0或小于等于0
        if (currentStock <= 0)
        {
            return StockStatusEnum.OutOfStock;
        }

        // 预警：当前库存小于等于最小库存阈值
        if (currentStock <= minStock.Value)
        {
            return StockStatusEnum.Warning;
        }

        // 正常：当前库存大于最小库存阈值
        return StockStatusEnum.Normal;
    }

    /// <summary>
    /// 更新物资库存状态和相关字段
    /// </summary>
    /// <param name="entity">物资实体</param>
    private void UpdateStockStatusAndFields(StoreMaterials entity)
    {
        // 计算可用库存（当前库存 - 预留库存）
        entity.AvailableStock = Math.Max(0, entity.CurrentStock - entity.ReservedStock);
        
        // 自动计算库存状态
        entity.StockStatus = CalculateStockStatus(entity.CurrentStock, entity.MinStock, entity.MaxStock);
        
        // 更新最后库存更新时间
        entity.LastStockUpdateTime = DateTime.Now;
    }

    #endregion

    /// <summary>
    /// 分页查询物资信息表 🔖
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("分页查询物资信息表")]
    [ApiDescriptionSettings(Name = "Page"), HttpPost]
    public async Task<SqlSugarPagedList<StoreMaterialsOutput>> Page(PageStoreMaterialsInput input)
    {
        input.Keyword = input.Keyword?.Trim();
        
        // 如果指定了分类ID，获取该分类及其所有下级分类的ID列表
        List<long> categoryIds = new List<long>();
        if (input.StoreTypeId > 0)
        {
            categoryIds = await GetCategoryAndChildrenIds(input.StoreTypeId);
        }
        
        // 先获取分页数据，不在SQL查询中构建分类路径
        var query = _storeMaterialsRep.AsQueryable()
            .WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.Name.Contains(input.Keyword))
            .WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name.Trim()))
            .WhereIF(input.Status.HasValue, u => u.Status == input.Status)
            .WhereIF(input.StoreTypeId > 0, u => categoryIds.Contains((long)u.StoreTypeId))
            .WhereIF(input.StockStatus.HasValue, u => u.StockStatus == input.StockStatus)
            .Select(u => new StoreMaterialsOutput
            {
                Id = u.Id,
                StoreTypeId = u.StoreTypeId,
                StoreTypeDisplayName = "", // 先设为空，后面异步构建
                Name = u.Name,
                Unit = u.Unit,
                Price = u.Price,
                MinStock = u.MinStock,
                MaxStock = u.MaxStock,
                OrderNo = u.OrderNo,
                Status = (StatusEnum)u.Status,
                CurrentStock = u.CurrentStock,
                AvailableStock = u.AvailableStock,
                ReservedStock = u.ReservedStock,
                InTransitStock = u.InTransitStock,
                StockStatus = (StockStatusEnum)u.StockStatus,
                LastStockUpdateTime = u.LastStockUpdateTime,
                Remark = u.Remark,
                TenantId = u.TenantId,
                IsDelete = u.IsDelete,
                CreateTime = u.CreateTime,
                UpdateTime = u.UpdateTime,
                CreateUserId = u.CreateUserId,
                CreateUserName = u.CreateUserName,
                UpdateUserId = u.UpdateUserId,
                UpdateUserName = u.UpdateUserName,
            });

        var pagedResult = await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);

        // 优化: 批量构建分类路径 (防止N+1查询)
        var typeIds = pagedResult.Items
            .Where(x => x.StoreTypeId.HasValue)
            .Select(x => x.StoreTypeId.Value)
            .Distinct()
            .ToList();

        var typePaths = typeIds.Count > 0
            ? await BatchBuildCategoryPathsAsync(typeIds)
            : new Dictionary<long, string>();

        // 在内存中关联数据
        foreach (var item in pagedResult.Items)
        {
            if (item.StoreTypeId.HasValue && typePaths.ContainsKey(item.StoreTypeId.Value))
            {
                item.StoreTypeDisplayName = typePaths[item.StoreTypeId.Value];
            }
        }

        return pagedResult;
    }

    /// <summary>
    /// 获取物资信息表详情 ℹ️
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("获取物资信息表详情")]
    [ApiDescriptionSettings(Name = "Detail"), HttpGet]
    public async Task<StoreMaterials> Detail([FromQuery] QueryByIdStoreMaterialsInput input)
    {
        return await _storeMaterialsRep.GetFirstAsync(u => u.Id == input.Id);
    }

    /// <summary>
    /// 增加物资信息表 ➕
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("增加物资信息表")]
    [ApiDescriptionSettings(Name = "Add"), HttpPost]
    public async Task<long> Add(AddStoreMaterialsInput input)
    {
        // 检查是否存在相同分类和物资名称的记录
        var existingRecord = await _storeMaterialsRep.GetFirstAsync(x => 
            x.StoreTypeId == input.StoreTypeId && 
            x.Name == input.Name && 
            x.IsDelete == false);
        
        if (existingRecord != null)
        {
            throw Oops.Oh($"该分类下已存在名称为 '{input.Name}' 的物资，不允许重复添加");
        }
        
        var entity = input.Adapt<StoreMaterials>();
        
        // 自动计算库存状态和相关字段
        UpdateStockStatusAndFields(entity);
        
        return await _storeMaterialsRep.InsertAsync(entity) ? entity.Id : 0;
    }

    /// <summary>
    /// 更新物资信息表 ✏️
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("更新物资信息表")]
    [ApiDescriptionSettings(Name = "Update"), HttpPost]
    public async Task Update(UpdateStoreMaterialsInput input)
    {
        // 检查是否存在相同分类和物资名称的记录（排除当前记录）
        var existingRecord = await _storeMaterialsRep.GetFirstAsync(x => 
            x.StoreTypeId == input.StoreTypeId && 
            x.Name == input.Name && 
            x.Id != input.Id && 
            x.IsDelete == false);
        
        if (existingRecord != null)
        {
            throw Oops.Oh($"该分类下已存在名称为 '{input.Name}' 的物资，不允许重复添加");
        }
        
        var entity = input.Adapt<StoreMaterials>();
        
        // 自动计算库存状态和相关字段
        UpdateStockStatusAndFields(entity);
        
        await _storeMaterialsRep.AsUpdateable(entity)
        .ExecuteCommandAsync();
    }

    /// <summary>
    /// 删除物资信息表 ❌
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("删除物资信息表")]
    [ApiDescriptionSettings(Name = "Delete"), HttpPost]
    public async Task Delete(DeleteStoreMaterialsInput input)
    {
        var entity = await _storeMaterialsRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
        await _storeMaterialsRep.FakeDeleteAsync(entity);   //假删除
        //await _storeMaterialsRep.DeleteAsync(entity);   //真删除
    }

    /// <summary>
    /// 批量删除物资信息表 ❌
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("批量删除物资信息表")]
    [ApiDescriptionSettings(Name = "BatchDelete"), HttpPost]
    public async Task<int> BatchDelete([Required(ErrorMessage = "主键列表不能为空")]List<DeleteStoreMaterialsInput> input)
    {
        var exp = Expressionable.Create<StoreMaterials>();
        foreach (var row in input) exp = exp.Or(it => it.Id == row.Id);
        var list = await _storeMaterialsRep.AsQueryable().Where(exp.ToExpression()).ToListAsync();
   
        return await _storeMaterialsRep.FakeDeleteAsync(list);   //假删除
        //return await _storeMaterialsRep.DeleteAsync(list);   //真删除
    }
    
    /// <summary>
    /// 设置物资信息表状态 🚫
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("设置物资信息表状态")]
    [ApiDescriptionSettings(Name = "SetStatus"), HttpPost]
    public async Task SetStoreMaterialsStatus(SetStoreMaterialsStatusInput input)
    {
        await _storeMaterialsRep.AsUpdateable().SetColumns(u => u.Status, input.Status).Where(u => u.Id == input.Id).ExecuteCommandAsync();
    }
    
    /// <summary>
    /// 获取下拉列表数据 🔖
    /// </summary>
    /// <returns></returns>
    [DisplayName("获取下拉列表数据")]
    [ApiDescriptionSettings(Name = "DropdownData"), HttpPost]
    public async Task<Dictionary<string, dynamic>> DropdownData(DropdownDataStoreTypeInput input)
    {
        //var storeTypeIdData = await _storeMaterialsRep.Context.Queryable<TreePOutput>()
        //    .Select(u => new TreeStoreTypeOutput {
        //        Value = u.Id,
        //        Label = $"{u.Name}"
        //    }, true).ToTreeAsync(u => u.Children, u => u.Pid, 0);
        //return new Dictionary<string, dynamic>
        //{
        //    { "storeTypeId", storeTypeIdData },
        //};
        
        // 修改分类
        return await _storeTypeService.DropdownData(input);
    }
    
    /// <summary>
    /// 导出物资信息表记录 🔖
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [DisplayName("导出物资信息表记录")]
    [ApiDescriptionSettings(Name = "Export"), HttpPost, NonUnify]
    public async Task<IActionResult> Export(PageStoreMaterialsInput input)
    {
        var list = (await Page(input)).Items?.Adapt<List<ExportStoreMaterialsOutput>>() ?? new();
        if (input.SelectKeyList?.Count > 0) list = list.Where(x => input.SelectKeyList.Contains(x.Id)).ToList();
        return ExcelHelper.ExportTemplate(list, "物资信息表导出记录");
    }
    
    /// <summary>
    /// 下载物资信息表数据导入模板 ⬇️
    /// </summary>
    /// <returns></returns>
    [DisplayName("下载物资信息表数据导入模板")]
    [ApiDescriptionSettings(Name = "Import"), HttpGet, NonUnify]
    public IActionResult DownloadTemplate()
    {
        return ExcelHelper.ExportTemplate(new List<ExportStoreMaterialsOutput>(), "物资信息表导入模板", (_, info) =>
        {
            if (nameof(ExportStoreMaterialsOutput.StoreTypeDisplayName) == info.Name) 
            {
                // 获取所有分类数据并构建完整路径
                var storeTypes = _sqlSugarClient.Queryable<StoreType>().ToList();
                var result = new List<string>();

                foreach (var storeType in storeTypes)
                {
                    var path = BuildCategoryPath(storeType.Id, storeTypes);
                    if (!string.IsNullOrEmpty(path))
                    {
                        result.Add(path);
                    }
                }
                return result.Distinct().ToList();
            }
            return null;
        });
    }
    
    private static readonly object _storeMaterialsImportLock = new object();
    /// <summary>
    /// 导入物资信息表记录 💾
    /// </summary>
    /// <returns></returns>
    [DisplayName("导入物资信息表记录")]
    [ApiDescriptionSettings(Name = "Import"), HttpPost, NonUnify, UnitOfWork]
    public IActionResult ImportData([Required] IFormFile file)
    {
        lock (_storeMaterialsImportLock)
        {
            var stream = ExcelHelper.ImportData<ImportStoreMaterialsInput, StoreMaterials>(file, (list, markerErrorAction) =>
            {
                _sqlSugarClient.Utilities.PageEach(list, 2048, pageItems =>
                {
                    // 链接 分类 - 支持完整路径匹配
                    var storeTypeIdLabelList = pageItems.Where(x => x.StoreTypeDisplayName != null).Select(x => x.StoreTypeDisplayName).Distinct().ToList();
                    if (storeTypeIdLabelList.Any()) {
                        // 获取所有分类数据
                        var allStoreTypes = _sqlSugarClient.Queryable<StoreType>().ToList();
                        var storeTypeIdLinkMap = new Dictionary<string, long?>();

                        // 为每个分类构建完整路径并建立映射
                        foreach (var storeType in allStoreTypes)
                        {
                            var fullPath = BuildCategoryPath(storeType.Id, allStoreTypes);
                            if (!string.IsNullOrEmpty(fullPath))
                            {
                                storeTypeIdLinkMap[fullPath] = storeType.Id;
                            }
                        }

                        pageItems.ForEach(e => {
                            e.StoreTypeId = storeTypeIdLinkMap.GetValueOrDefault(e.StoreTypeDisplayName ?? "");
                            if (e.StoreTypeId == null) e.Error = "分类链接失败";
                        });
                    }
                    
                    // 校验并过滤必填基本类型为null的字段
                    var rows = pageItems.Where(x => {
                        if (!string.IsNullOrWhiteSpace(x.Error)) return false;
                        if (x.StoreTypeId == null){
                            x.Error = "分类不能为空";
                            return false;
                        }
                        if (x.MinStock == null){
                            x.Error = "最小库存不能为空";
                            return false;
                        }
                        if (x.MaxStock == null){
                            x.Error = "最大库存不能为空";
                            return false;
                        }
                        
                        // 检查是否存在相同分类和物资名称的记录
                        var existingRecord = _storeMaterialsRep.GetFirst(m => 
                            m.StoreTypeId == x.StoreTypeId && 
                            m.Name == x.Name && 
                            m.IsDelete == false);
                        
                        if (existingRecord != null)
                        {
                            x.Error = $"该分类下已存在名称为 '{x.Name}' 的物资，不允许重复添加";
                            return false;
                        }
                        
                        return true;
                    }).Adapt<List<StoreMaterials>>();
                    
                    // 为每条记录自动计算库存状态和相关字段
                    rows.ForEach(entity => UpdateStockStatusAndFields(entity));
                    
                    var storageable = _storeMaterialsRep.Context.Storageable(rows)
                        .SplitError(it => it.Item.StoreTypeId == null, "分类不能为空")
                        .SplitError(it => string.IsNullOrWhiteSpace(it.Item.Name), "物资名称不能为空")
                        .SplitError(it => it.Item.Name?.Length > 128, "物资名称长度不能超过128个字符")
                        .SplitError(it => string.IsNullOrWhiteSpace(it.Item.Unit), "单位不能为空")
                        .SplitError(it => it.Item.Unit?.Length > 32, "单位长度不能超过32个字符")
                        .SplitError(it => it.Item.Price == null, "单价不能为空")
                        .SplitError(it => it.Item.MinStock == null, "最小库存不能为空")
                        .SplitError(it => it.Item.MaxStock == null, "最大库存不能为空")
                        .SplitInsert(_=> true) // 没有设置唯一键代表插入所有数据
                        .ToStorage();
                    
                    storageable.AsInsertable.ExecuteCommand();// 不存在插入
                    storageable.AsUpdateable.UpdateColumns(it => new
                    {
                        it.StoreTypeId,
                        it.Name,
                        it.Unit,
                        it.Price,
                        it.MinStock,
                        it.MaxStock,
                        it.CurrentStock,
                        it.AvailableStock,
                        it.ReservedStock,
                        it.InTransitStock,
                        it.StockStatus,
                        it.LastStockUpdateTime,
                    }).ExecuteCommand();// 存在更新
                    
                    // 标记错误信息
                    markerErrorAction.Invoke(storageable, pageItems, rows);
                });
            });
            
            return stream;
        }
    }
}
